﻿using System;
using System.Drawing;
using System.Threading;
using System.Collections.Generic;

using COUP.Core.Enums;
using COUP.Core.Networking;

namespace COUP.Core.Entities
{
    public unsafe class Monster : iEntity
    {
        #region Private Fields

        private int _Identifier, _ModelID, _Map, _Hp, _MaxHp, _MaxTravelDistance, _MovementSpeed;
        private byte _Level, _Action;
        private string _Name;

        private Direction _angle;
        private Sector _currentSector;
        private Rectangle _area;
        private Rectangle _visible;
        private Point _location, _spawnLocation;

        #endregion

        public Monster(MonsterTypeEntry Type, int UniqueID, int x, int y)
        {
            _Identifier = UniqueID;
            _location = new Point(x, y);
            _area = new Rectangle(_location, new Size(1, 1));
            _visible = new Rectangle(_location, new Size(6, 6));
            _Name = Type.Name;
            _ModelID = 104;
            _Level = Type.Level;
            _Hp = 33;
            _Action = 100;
            _MovementSpeed = 800;
            _angle = Direction.SouthEast;
            Alive = true;
        }

        public Monster(int UniqueID, int x, int y)
        {
            _Identifier = 300000 + UniqueID;
            _location = new Point(x, y);
            _area = new Rectangle(_location, new Size(1, 1));
            _visible = new Rectangle(_location, new Size(6, 6));
            _Name = "Pheasant";
            _ModelID = 104;
            _Level = 1;
            _Hp = 33;
            _Action = 100;
            _MovementSpeed = 800;
            _angle = Direction.SouthEast;
            Alive = true;
        }

        #region Public Properties

        public int UniqueID
        {
            get { return _Identifier; }
        }

        public int Model
        {
            get { return _ModelID; }
        }

        public string Name
        {
            get { return _Name; }
        }

        public Point Location
        {
            get { return _location; }
        }

        public Rectangle Area
        {
            get { return _area; }
        }

        public Rectangle VisibleArea
        {
            get { return _visible; }
        }

        public Sector CurrentSector
        {
            get { return _currentSector; }
            set { _currentSector = value; }
        }

        public Direction Angle
        {
            get { return _angle; }
        }

        public int X
        {
            get { return _location.X; }
            set
            {
                _location.X = value;
                InformVisibleArea();
            }
        }

        public int Y
        {
            get { return _location.Y; }
            set
            {
                _location.Y = value;
                InformVisibleArea();
            }
        }

        public int Health
        {
            get { return _Hp; }
            set
            {
                _Hp = value;

                if (_Hp <= 0)
                {
                    Alive = false;
                    //Mark monster as requiring respawn
                }
            }
        }

        public byte Level
        {
            get { return _Level; }
        }

        public byte Action
        {
            get { return _Action; }
        }

        public bool Alive { get; set; }

        public List<iEntity> GetLocalEntities
        {
            get { return CurrentSector.Query(VisibleArea); }
        }

        #endregion

        #region Internal Methods

        internal void SendToLocal(Packet Data)
        {
            foreach (iEntity Target in GetLocalEntities)
            {
                if (Target is Character)
                {
                    Character Char = Target as Character;

                    Char.Parent.Send(new Packet(Data.Buffer));
                }
            }
        }

        internal void MoveToTarget(object State)
        {
            //Check that we are alive and the target is in range
            Character Target = State as Character;
            if (!Alive || !VisibleArea.IntersectsWith(Target.Area))
            {
                Console.WriteLine("[GameServer] Monster '" + UniqueID + "' Target not in visible Area");
                return;
            }

            //Initialize some things we will need
            bool TargetReached = false;
            Point Start = Location;
            Point Finsh = Target.Location;

            //Get the path
            Point[] Path = Calculations.GetPath(CurrentSector.MapParent, Location, Target.Location);
            if (Path == null)
            {
                //Not able to get to target so leave
                Console.WriteLine("[GameServer] Monster '" + UniqueID + "' Unable to Reach Target");
                return;
            }

            int x = 0;

            //Begin moving
            while (!TargetReached)
            {
                //Again that we are alive and the target is in range
                if (!Alive || !VisibleArea.IntersectsWith(Target.Area))
                {
                    Console.WriteLine("[GameServer] Monster '" + UniqueID + "' Target not in visible Area");
                    return;
                }

                //If the target has moved re-calculate the path
                if (Target.Location != Finsh)
                {
                    Start = Location;
                    Finsh = Target.Location;
                    Path = Calculations.GetPath(CurrentSector.MapParent, Location, Target.Location);
                    if (Path == null)
                    {
                        //Not able to get to target so leave
                        Console.WriteLine("[GameServer] Monster '" + UniqueID + "' Unable to Reach Target");
                        return;
                    }

                    x = 0;
                }

                sbyte[] Movement = new sbyte[2] { Convert.ToSByte(Path[x].X - X), Convert.ToSByte(Path[x].Y - Y) };

                for (int y = 0; y < 8; y++)
                {
                    if (Settings.PathDirection[y, 0] == Movement[0] && Settings.PathDirection[y, 1] == Movement[1])
                    {
                        _angle = (Direction)y;
                        break;
                    }
                }

                //Start moving down the path
                X = Path[x].X;
                Y = Path[x].Y;
                InformVisibleArea();

                SendToLocal(new MovementPacket(UniqueID, _angle, Speed.Walk));

                //If we reach the end then leave the loop
                if (Location == Finsh)
                    TargetReached = true;

                x++;
                Thread.Sleep(_MovementSpeed);

                while (!Monitor.TryEnter(Console.Out)) { Thread.Sleep(1); }
                Console.WriteLine("[GameServer] Monster '" + _Identifier + "' Moving");
                Monitor.Exit(Console.Out);
            }

            while (!Monitor.TryEnter(Console.Out)) { Thread.Sleep(1); }
            Console.WriteLine("[GameServer] Monster '" + _Identifier + "' Target Reached");
            Monitor.Exit(Console.Out);

            //Target reached so leave this thread and join a different one
        }

        internal void Respawn()
        {

        }

        internal void InformSector()
        {
            if (_currentSector == null)
                return;

            if (!_currentSector.Bounds.Contains(_location))
            {
                _currentSector.Move(this);
            }
        }

        internal void InformVisibleArea()
        {
            _visible.Location = Location;
            _visible.Offset(-3, -3);

            _area.Location = Location;

            InformSector();
        }

        #endregion
    }
}
